package com.sean.order.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/hello")
@Slf4j
public class HelloController {
    private static final String HELLO_RESOURCE_NAME = "hello";
    private static final String USER_RESOURCE_NAME = "user";
    private static final String DEGRADE_RESOURCE_NAME = "degrade";

    @PostConstruct  // init-method（生命周期回调函数）
    private static void initFlowRules(){
        // 流控规则
        List<FlowRule> flowRules = new ArrayList<>();
        // 流控规则
        FlowRule flowRule = new FlowRule();
        // 设置受保护的资源
        flowRule.setResource(HELLO_RESOURCE_NAME);
        // 设置流控规则 QPS（每秒访问数）
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // Set limit QPS to 20.
        flowRule.setCount(1);
        flowRules.add(flowRule);

        // 流控规则
        FlowRule flowRule2 = new FlowRule();
        // 设置受保护的资源
        flowRule2.setResource(USER_RESOURCE_NAME);
        // 设置流控规则 QPS（每秒访问数）
        flowRule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // Set limit QPS to 20.
        flowRule2.setCount(1);
        flowRules.add(flowRule2);

        // 加载配置好的规则
        FlowRuleManager.loadRules(flowRules);

        // 降级规则
        List<DegradeRule> degradeRules = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource(DEGRADE_RESOURCE_NAME);
        // 规则策略 (异常数)
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 触发熔断异常数
        degradeRule.setCount(2);
        // 触发熔断最小请求数
        degradeRule.setMinRequestAmount(2);
        // 统计时长：1分钟内2次异常
        degradeRule.setStatIntervalMs(60 * 1000);
        // 熔断持续时长：持续时间内的请求都进行降级处理
        // 持续时间后会进入半开状态：恢复接口调用，但第一次就异常就会触发熔断
        degradeRule.setTimeWindow(10);

        degradeRules.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRules);
    }

    @RequestMapping(value = "/hello")
    public String hello() {
        Entry entry = null;
        try {
            // sentinel 针对资源进行限制，接口就是资源
            entry = SphU.entry(HELLO_RESOURCE_NAME);
            // 被保护的业务逻辑
            String str = "hello world";
            log.info("====="+ str + "=====");
            return str;
        } catch (BlockException ex) {
            // 资源访问阻止，被限流或被降级
            // 进行相应的处理操作
            log.info("block!");
            return "被流控了!";
        } catch (Exception ex) {
            // 若需要配置降级规则，需要通过这种方式记录业务异常
            Tracer.traceEntry(ex, entry);
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return null;
    }

    @GetMapping("/user/{id}")
    @SentinelResource(value = USER_RESOURCE_NAME, blockHandler = "handleBlockExceptionForgetUserById")
    public String getUserById(@PathVariable("id") Integer id){
        return "获取用户：id = " + id;
    }

    public String handleBlockExceptionForgetUserById(Integer id, BlockException exception){
        exception.printStackTrace();
        return "被流控了：id = " + id;
    }

    @GetMapping("/degrade/{id}")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME, entryType = EntryType.IN, blockHandler = "blockHandlerForDegrade")
    public String degrade(String id) throws Exception{
        // 异常数 \ 比例
        throw new RuntimeException("异常");
    }

    public String blockHandlerForDegrade(String id, BlockException exception){
        exception.printStackTrace();
        return "被降级了: id = " + id;
    }


}
